const ping = (new Uint8Array([0x06, 0x00, 0x70, 0x69, 0x6E, 0x67])).buffer

function str2buf(str) {
	var bytes = new Array(); 
	var len,c;
  len = str.length;
  bytes.push(len & 0xFF)
  bytes.push((len>>8) & 0xFF)
	for(var i = 0; i < len; i++) {
		c = str.charCodeAt(i);
		if(c >= 0x010000 && c <= 0x10FFFF){
			bytes.push(((c >> 18) & 0x07) | 0xF0);
			bytes.push(((c >> 12) & 0x3F) | 0x80);
			bytes.push(((c >> 6) & 0x3F) | 0x80);
			bytes.push((c & 0x3F) | 0x80);
		}else if(c >= 0x000800 && c <= 0x00FFFF){
			bytes.push(((c >> 12) & 0x0F) | 0xE0);
			bytes.push(((c >> 6) & 0x3F) | 0x80);
			bytes.push((c & 0x3F) | 0x80);
		}else if(c >= 0x000080 && c <= 0x0007FF){
			bytes.push(((c >> 6) & 0x1F) | 0xC0);
			bytes.push((c & 0x3F) | 0x80);
		}else{
			bytes.push(c & 0xFF);
		}
  }
  var array = new Int8Array(bytes.length);
  for(var i in bytes){
    array[i] =bytes[i];
  }
	return array.buffer;
}
function buf2str(arr, offset, len){
    if(typeof arr === 'string') {  
        return arr.substring(offset, offset+len);
    }  
    if (len == undefined || !len) {
      console.log("invalid-len")
      len = arr.byteLength - offset
    }
    var dataview=new DataView(arr.buffer, offset, len);
    var ints=new Uint8Array(len);
    for(var i=0;i<ints.length;i++){
      ints[i]=dataview.getUint8(i);
    }
    arr=ints;
    var str = '',  
        _arr = arr;  
    for(var i = 0; i < _arr.length; i++) {  
        var one = _arr[i].toString(2),  
            v = one.match(/^1+?(?=0)/);  
        if(v && one.length == 8) {  
            var byteLength = v[0].length;  
            var store = _arr[i].toString(2).slice(7 - byteLength);  
            for(var st = 1; st < byteLength; st++) {  
                store += _arr[st + i].toString(2).slice(2);  
            }  
            str += String.fromCharCode(parseInt(store, 2));  
            i += byteLength - 1;  
        } else {  
            str += String.fromCharCode(_arr[i]);  
        }  
    }  
    return str; 
}
function uint8Add(a,b) {
  if (!a)   return b
  let c = new Uint8Array(a.length+b.length)
  c.set(a)
  c.set(b, a.length)
  return c
}
class DevLink {
  constructor(plant) {
    this.plant = plant
    this.valid = false
    this.parse = {}
    this.reStartTimer = null
    this.handleClose = ()=>{
      this.valid = false
      console.log("on-close", this.tcp)
      this.tcp.offError(this.handleError)
      this.tcp.offMessage(this.handleMessage)
      this.tcp.offConnect(this.handleConnect)
      this.tcp.offClose(this.handleClose)
      this.tcp.close()
      this.tcp = null
      this._clearRecvTimeout()
      this._restartTimer() 
      if (this.handleConnectClose) this.handleConnectClose()
    }
    this.handleError = (err)=>{
      this.valid = false
      console.log("on-error", err)
      this._restartTimer() 
    }
    this.handleConnect = ()=>{
      console.log("on-connect")
      this.valid = true
    }
    this.handleMessage = ({message})=>{
      let str = this.doRecv(message)
      if (str == null) return
      console.log("msg", str)
      this.handleDevInfoCb && this.handleDevInfoCb(str)
    }
  }
  start() {
    if (this.valid)  return
    this.tcp = wx.createTCPSocket()
    if (!this.tcp)  return
    this.tcp.onClose(this.handleClose)
    this.tcp.onConnect(this.handleConnect)
    this.tcp.onMessage(this.handleMessage)
    this.tcp.onError(this.handleError)
    //this.tcp.connect({address: '81.70.98.133', port: 50617})
    this.tcp.connect({address: 'wg.jlntech.cn', port: 50617})
  }
  onDevInfo(x) {
    this.handleDevInfoCb = x
  }
  onCloseNotify(x) {
    this.handleConnectClose = x
  }
  doRecv(message) {
    let str = this._doRecv(message)
    if (str != null) this._clearRecvTimeout()
    if (this.parse.buf) {
      console.log("wait-more-data")
      if (!this.parse.timer) {
        this.parse.timer = setTimeout(()=>{
          console.log("recv-timeout")
          this.tcp.close()
        }, 200)
      }
    }
    return str
  }
  _clearRecvTimeout() {
    if (this.parse.timer) {
      console.log("recv-timeout")
      clearTimeout(this.parse.timer)
      this.parse.timer = null
    }
  }
  _restartTimer() {
      this._clearRestartTimer() 
      this.reStartTimer = setTimeout(()=>this.start(), 3000)
  }
  _clearRestartTimer() {
    if (this.restartTimer) {
      clearTimeout(this.restartTimer)
      this.restartTimer = null
    }
  }
  _doRecv(message) {
    let rcvData = new Uint8Array(message)
    let data = this.parse.buf = uint8Add(this.parse.buf, rcvData)
    console.log("on-message", rcvData, data)
    let off = 0
    let len = this.parse.len
    if (!len) {
      if (data.length<2) {
        console.log("wait-len", len)
        return null
      }  
      const dv = new DataView(data.buffer)
      len = dv.getUint16(0,true)
      off = 2
      this.parse.buf = data = data.subarray(2)
      this.parse.len = len
    }
    if (data.byteLength < len) {
      console.log("wait-body", data.byteLength, len)
      return null
    } 
    if (data.byteLength > len) {
      this.parse.buf = data.subarray(len)
      console.log("body-recv continue", this.parse.buf.byteLength)
    } else {
      console.log("body-recv over")
      this.parse.buf = null
    }
    this.parse.len = 0

    return buf2str(data, off, len)
  }
  write(message) {
    if (!this.valid)  return
    console.log("write", message)
    if (typeof message == "object") message = JSON.stringify(message)
    this.tcp.write(str2buf(message))
  }
  register(mac) {
    this.write(mac)
  }
}

module.exports = {
  DevLink
}
